handler机制的原理

您所在的位置:网站首页 android 为什么handler不会阻塞 handler机制的原理

handler机制的原理

2024-07-14 15:31| 来源: 网络整理| 查看: 265

本文作者

作者:N0tExpectErr0r

链接:

https://xiaozhuanlan.com/topic/0843791256

本文由作者授权发布。

之前还推送过一篇也很赞的文章,可以配合学习,比较香:

关于Handler 的这 15 个问题,你都清楚吗?

大家应该都知道,Android 的消息机制是基于 Handler 实现的。 还记得一年前的自己就看了几篇博客,知道了 Handler、Looper、MessageQueue 就自以为了解了 Handler 的原理。但其实看源码的过程中慢慢就会发现,Handler 的内容可不止这点, 像同步屏障、 Handler 的 native 层的阻塞唤醒机制等等这些知识以前就没有理解清楚。 因此写下这篇文章,从头开始重塑对 Handler 的印象。

Handler 采用的是一种生产者-消费者模型,Handler 就是生产者,通过它可以生产需要执行的任务。而 Looper 则是消费者,不断从 MessageQueue 中取出 Message 对这些消息进行消费,下面我们看一下其具体的实现。

1 发送消息 post & sendMessage

首先我们都知道,Handler 对外主要有两种方式来实现在其所在 Looper 所在线程执行指定 Runnable——post 及 sendMessage,它们都有对应的 delay 方法 。而不论是 post 还是 sendMessage,都会调用到 sendMessageDelayed方法。比如下面是 post 方法的实现:

public final boolean post(Runnable r) {    return sendMessageDelayed(getPostMessage(r), 0);}

可以看到它其实调用的仍然是 sendMessageDelayed 方法,只是通过 getPostMessage 方法将这个 Runnable 包装成了一个 Message 对象。

private static Message getPostMessage(Runnable r) {    Message m = Message.obtain();    m.callback = r;    return m;}

这个包装出来的 Message 将 callback 设置为了对应的 Runnable。

而所有的 sendMessage 和 post 方法,实际上最后都通过 sendMessageDelayed 方法调用到了 sendMessageAtTime 方法:

public final boolean sendMessageDelayed(Message msg, long delayMillis) {    if (delayMillis 0) {        delayMillis = 0;    }    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);}

在 sendMessageAtTime 中,它首先通过 mQueue 拿到了对应的 MessageQueue 对象,然后调用了 enqueueMessage 方法将 Message 发送至 MessageQueue 中。

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {    MessageQueue queue = mQueue;    if (queue == null) {        RuntimeException e = new RuntimeException(                this + " sendMessageAtTime() called with no mQueue");        Log.w("Looper", e.getMessage(), e);        return false;    }    return enqueueMessage(queue, msg, uptimeMillis);}

最后实际上是调用到了 MessageQueue 的 enqueueMessage 方法将这个消息传入了 MessageQueue。它将 Message 的 target 设置为了当前的 Handler,同时要注意看到,这里在 enqueueMessage 之前先判断了一下 mAsynchronous 是否为 true,若为 true 则将该 Message 的 Asynchronous 置为 true。

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {    msg.target = this;    if (mAsynchronous) {        msg.setAsynchronous(true);    }    return queue.enqueueMessage(msg, uptimeMillis);}

那这个 mAsynchronous 是什么时候被赋值的呢?点进去看可以发现它的赋值是在 Handler 的构造函数中进行的。也就是说创建的 Handler 时若将 async 置为 true 则该 Handler 发出的 Message 都会被设为 Async,也就是『异步消息』。

public Handler(Callback callback, boolean async)

public Handler(Looper looper, Callback callback, boolean async)

关于异步消息和同步消息是什么,我们放在后面讨论。

Handler 有很多种构造函数,但其他的构造函数最后仍然会调用到上述的两种构造函数,其 async 默认会被设置为 false。

让我们看看上述的两种构造函数:

public Handler(Callback callback, boolean async) {    if (FIND_POTENTIAL_LEAKS) {        final Class extends Handler> klass = getClass();        if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&                (klass.getModifiers() & Modifier.STATIC) == 0) {            Log.w(TAG, "The following Handler class should be static or leaks might occur: " +                klass.getCanonicalName());        }    }    mLooper = Looper.myLooper();    if (mLooper == null) {        throw new RuntimeException(            "Can't create handler inside thread that has not called Looper.prepare()");    }    mQueue = mLooper.mQueue;    mCallback = callback;    mAsynchronous = async;}

可以看到这个构造函数主要是对 mLooper、mQueue、mCallback、mAsynchronous 进行赋值,其中 mLooper 是通过 Looper.myLooper 方法获取到的,另一种构造函数除了 Looper 是通过外部传入以外和这个构造函数的实现差不多。

同时我们还能看出,mQueue 这个 MessageQueue 是 Looper 对象内部的一个成员变量。

2 消息入队 enqueueMessage

我们接着看看 Handler 发送了消息后 MessageQueue 的 enqueueMessage 方法:

boolean enqueueMessage(Message msg, long when) {    if (msg.target == null) {        throw new IllegalArgumentException("Message must have a target.");    }    if (msg.isInUse()) {        throw new IllegalStateException(msg + " This message is already in use.");    }    synchronized (this) {        if (mQuitting) {            IllegalStateException e = new IllegalStateException(                    msg.target + " sending message to a Handler on a dead thread");            Log.w(TAG, e.getMessage(), e);            msg.recycle();            return false;        }        msg.markInUse();        msg.when = when;        Message p = mMessages;        boolean needWake;        if (p == null || when == 0 || when when) {            // New head, wake up the event queue if blocked.            msg.next = p;            mMessages = msg;            needWake = mBlocked;        } else {            // Inserted within the middle of the queue.  Usually we don't have to wake            // up the event queue unless there is a barrier at the head of the queue            // and the message is the earliest asynchronous message in the queue.            needWake = mBlocked && p.target == null && msg.isAsynchronous();            Message prev;            for (;;) {                prev = p;                p = p.next;                if (p == null || when when) {                    break;                }                if (needWake && p.isAsynchronous()) {                    needWake = false;                }            }            msg.next = p; // invariant: p == prev.next            prev.next = msg;        }        // We can assume mPtr != 0 because mQuitting is false.        if (needWake) {            nativeWake(mPtr);        }    }    return true;}

可以看到, MessageQueue 实际上里面维护了一个 Message 构成的链表,每次插入数据都会按时间顺序进行插入,也就是说 MessageQueue 中的 Message 都是按照时间排好序的,这样的话就使得循环取出 Message 的时候只需要一个个地从前往后拿即可,这样 Message 都可以按时间先后顺序被消费。

最后在需要唤醒的情况下会调用 nativeWake 这个 native 方法用于进行唤醒,这些和唤醒机制有关的代码我们后面再进行讨论,先暂时放在一边。

3 消息循环

那么我们看看 Looper.myLooper 方法是如何获取到 Looper 对象的呢?

public static @Nullable Looper myLooper() {    return sThreadLocal.get();}

可以看出来,这个 Looper 对象是通过 sThreadLocal.get 方法获取到的,也就是说这个 Looper 是一个线程独有的变量,每个线程具有一个不同的 Looper。

那么这个 Looper 对象是何时创建又何时放入这个 ThreadLocal 中的呢?

我们通过跟踪可以发现它实际上是通过 Looper.prepare 方法放入 ThreadLocal 中的:

private static void prepare(boolean quitAllowed) {    if (sThreadLocal.get() != null) {        throw new RuntimeException("Only one Looper may be created per thread");    }    sThreadLocal.set(new Looper(quitAllowed));}

那按道理来说我们在应用程序中并没有调用 Looper.prepare 方法,为何还能通过主线程的 Handler 发送 Message 到主线程呢?

其实这个 Looper.prepare 方法在主线程创建时就已经被创建并调用了 prepare 方法进行设置,具体我们可以看到 ActivityThread 类的 main 函数:

public static void main(String[] args) {    // ...    Looper.prepareMainLooper();    // ...    Looper.loop();    // ...}

这个 main 函数其实就是我们进程的入口,可以看出来它首先调用了 Looper.prepareMainLooper 创建了主线程的 Looper 并传入 ThreadLocal,自此我们就可以在主线程发送消息了。

为什么要这样设计呢?因为其实我们的 View 绘制事件等都是通过主线程的 Handler 来进行调度的。

我们接着看到 Looper.loop 方法:

public static void loop() {    final Looper me = myLooper();    if (me == null) {        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");    }    final MessageQueue queue = me.mQueue;    // Make sure the identity of this thread is that of the local process,    // and keep track of what that identity token actually is.    Binder.clearCallingIdentity();    final long ident = Binder.clearCallingIdentity();    for (;;) {        Message msg = queue.next(); // might block        if (msg == null) {            // No message indicates that the message queue is quitting.            return;        }        // ...        final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();        final long end;        try {            msg.target.dispatchMessage(msg);            end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();        } finally {            // ...        }        // ...        msg.recycleUnchecked();    }}

这里其实是一个死循环,它的主要作用是遍历 MessageQueue,获取到 Looper 及 MessageQueue 后,不断通过 MessageQueue 的 next 方法获取到消息列表中的下一个 Message,之后调用了 Message 的 target 的 dispatchMessage 方法对 Message 进行消费,最后对 Message 进行了回收。

通过上面的代码可以看出,Looper 主要的作用是遍历 MessageQueue,每找到一个 Message 都会调用其 target 的 dispatchMessage 对该消息进行消费,这里的 target 也就是我们之前发出该 Message 的 Handler。

4 消息遍历

我们接着看到消息的遍历过程,它不断地从 MessageQueue 中调用 next 方法拿到消息,并对其进行消费,那我们具体看看 next 的过程:

Message next() {    final long ptr = mPtr;    if (ptr == 0) {        return null;    }    int pendingIdleHandlerCount = -1;    int nextPollTimeoutMillis = 0;    for (;;) {        if (nextPollTimeoutMillis != 0) {            Binder.flushPendingCommands();        }        // 1        nativePollOnce(ptr, nextPollTimeoutMillis);        synchronized (this) {            final long now = SystemClock.uptimeMillis();            Message prevMsg = null;            Message msg = mMessages;            // 2            if (msg != null && msg.target == null) {                do {                    prevMsg = msg;                    msg = msg.next;                } while (msg != null && !msg.isAsynchronous());            }            if (msg != null) {                // 3                if (now                     nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);                } else {                    // Got a message.                    mBlocked = false;                    if (prevMsg != null) {                        prevMsg.next = msg.next;                    } else {                        mMessages = msg.next;                    }                    msg.next = null;                    if (DEBUG) Log.v(TAG, "Returning message: " + msg);                    msg.markInUse();                    return msg;                }            } else {                // No more messages.                nextPollTimeoutMillis = -1;            }            if (mQuitting) {                dispose();                return null;            }            // 4            if (pendingIdleHandlerCount 0                    && (mMessages == null || now                 pendingIdleHandlerCount = mIdleHandlers.size();            }            if (pendingIdleHandlerCount 


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3